home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / gr564s.zip / SRC / MAKETIME.C < prev    next >
C/C++ Source or Header  |  1992-09-05  |  8KB  |  322 lines

  1. /* maketime - yield time_t from struct tm yielded by partime */
  2.  
  3. /* Copyright 1992 by Paul Eggert
  4.    Distributed under license by the Free Software Foundation, Inc.
  5.  
  6. This file is part of RCS.
  7.  
  8. RCS is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2, or (at your option)
  11. any later version.
  12.  
  13. RCS is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. GNU General Public License for more details.
  17.  
  18. You should have received a copy of the GNU General Public License
  19. along with RCS; see the file COPYING.  If not, write to
  20. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22. Report problems and direct all questions to:
  23.  
  24.     rcs-bugs@cs.purdue.edu
  25.  
  26. */
  27.  
  28. #include "rcsbase.h"
  29.  
  30. libId(maketId, "$Id: maketime.c,v 5.7 1992/05/31 08:29:18 eggert Exp $")
  31.  
  32. /*
  33. * For maximum portability, use only localtime and gmtime.
  34. * Make no assumptions about the epoch or the range of time_t values.
  35. * Avoid mktime because it's not universal and because there's no easy,
  36. * portable way for mktime to yield the inverse of gmtime.
  37. */
  38.  
  39. #define TM_YEAR_ORIGIN 1900
  40.  
  41. #define LOCAL_TIME (48*60)
  42.  
  43.     static int
  44. isleap(y)
  45.     int y;
  46. {
  47.     return (y&3) == 0  &&  (y%100 != 0 || y%400 == 0);
  48. }
  49.  
  50. static int const month_yday[] = {
  51.     /* days in year before start of months 0-12 */
  52.     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
  53. };
  54.  
  55. /* Yield the number of days in TM's month.  */
  56.     static int
  57. month_days(tm)
  58.     struct tm const *tm;
  59. {
  60.     int m = tm->tm_mon;
  61.     return month_yday[m+1] - month_yday[m]
  62.         + (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN));
  63. }
  64.  
  65. /*
  66. * Convert UNIXTIME to struct tm form.
  67. * Use gmtime if available and if !LOCALZONE, localtime otherwise.
  68. */
  69.     static struct tm *
  70. time2tm(unixtime, localzone)
  71.     time_t unixtime;
  72.     int localzone;
  73. {
  74.     struct tm *tm;
  75. #    if TZ_must_be_set
  76.         static char const *TZ;
  77.         if (!TZ  &&  !(TZ = getenv("TZ")))
  78.             faterror("TZ is not set");
  79. #    endif
  80.     if (localzone  ||  !(tm = gmtime(&unixtime)))
  81.         tm = localtime(&unixtime);
  82.     return tm;
  83. }
  84.  
  85. /* Yield A - B, measured in seconds.  */
  86.     static time_t
  87. difftm(a, b)
  88.     struct tm const *a, *b;
  89. {
  90.     int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
  91.     int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
  92.     return
  93.         (
  94.             (
  95.                 (
  96.                     /* difference in day of year */
  97.                        a->tm_yday - b->tm_yday
  98.                     /* + intervening leap days */
  99.                     +  ((ay >> 2) - (by >> 2))
  100.                     -  (ay/100 - by/100)
  101.                     +  ((ay/100 >> 2) - (by/100 >> 2))
  102.                     /* + difference in years * 365 */
  103.                     +  (time_t)(ay-by) * 365
  104.                 )*24 + (a->tm_hour - b->tm_hour)
  105.             )*60 + (a->tm_min - b->tm_min)
  106.         )*60 + (a->tm_sec - b->tm_sec);
  107. }
  108.  
  109. /*
  110. * Adjust T by adding MINUTES.  MINUTES must be at most 24 hours' worth.
  111. * Adjust only T's year, mon, mday, hour and min members;
  112. * plus adjust wday if it is not negative.
  113. */
  114.     static void
  115. adjzone(t, minutes)
  116.     register struct tm *t;
  117.     int minutes;
  118. {
  119.     if ((t->tm_min += minutes) < 0) {
  120.         if ((t->tm_hour -= (59-t->tm_min)/60) < 0) {
  121.         t->tm_hour += 24;
  122.         if (0 <= t->tm_wday  &&  --t->tm_wday < 0)
  123.             t->tm_wday = 6;
  124.         if (--t->tm_mday <= 0) {
  125.             if (--t->tm_mon < 0) {
  126.             --t->tm_year;
  127.             t->tm_mon = 11;
  128.             }
  129.             t->tm_mday = month_days(t);
  130.         }
  131.         }
  132.         t->tm_min += 24*60;
  133.     } else
  134.         if (24 <= (t->tm_hour += t->tm_min/60)) {
  135.         t->tm_hour -= 24;
  136.         if (0 <= t->tm_wday  &&  ++t->tm_wday == 7)
  137.             t->tm_wday = 0;
  138.         if (month_days(t) < ++t->tm_mday) {
  139.             if (11 < ++t->tm_mon) {
  140.             ++t->tm_year;
  141.             t->tm_mon = 0;
  142.             }
  143.             t->tm_mday = 1;
  144.         }
  145.         }
  146.     t->tm_min %= 60;
  147. }
  148.  
  149. /*
  150. * Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
  151. * Use only TM's year, mon, mday, hour, min, and sec members.
  152. * Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
  153. * Yield -1 on failure (e.g. a member out of range).
  154. * Posix 1003.1-1990 doesn't allow leap seconds, but some implementations
  155. * have them anyway, so allow them if localtime/gmtime does.
  156. */
  157.     static time_t
  158. tm2time(tm, localzone)
  159.     struct tm *tm;
  160.     int localzone;
  161. {
  162.     /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime.  */
  163.     static time_t t_cache[2];
  164.     static struct tm tm_cache[2];
  165.  
  166.     time_t d, gt;
  167.     struct tm const *gtm;
  168.     /*
  169.     * The maximum number of iterations should be enough to handle any
  170.     * combinations of leap seconds, time zone rule changes, and solar time.
  171.     * 4 is probably enough; we use a bigger number just to be safe.
  172.     */
  173.     int remaining_tries = 8;
  174.  
  175.     /* Avoid subscript errors.  */
  176.     if (12 <= (unsigned)tm->tm_mon)
  177.         return -1;
  178.  
  179.     tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
  180.         -  (tm->tm_mon<2  ||  ! isleap(tm->tm_year + TM_YEAR_ORIGIN));
  181.  
  182.     /* Make a first guess.  */
  183.     gt = t_cache[localzone];
  184.     gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone);
  185.  
  186.     /* Repeatedly use the error from the guess to improve the guess.  */
  187.     while ((d = difftm(tm, gtm)) != 0) {
  188.         if (--remaining_tries == 0)
  189.             return -1;
  190.         gt += d;
  191.         gtm = time2tm(gt,localzone);
  192.     }
  193.     t_cache[localzone] = gt;
  194.     tm_cache[localzone] = *gtm;
  195.  
  196.     /*
  197.     * Check that the guess actually matches;
  198.     * overflow can cause difftm to yield 0 even on differing times,
  199.     * or tm may have members out of range (e.g. bad leap seconds).
  200.     */
  201.     if (   tm->tm_year ^ gtm->tm_year
  202.         |  tm->tm_mon  ^ gtm->tm_mon
  203.         |  tm->tm_mday ^ gtm->tm_mday
  204.         |  tm->tm_hour ^ gtm->tm_hour
  205.         |  tm->tm_min  ^ gtm->tm_min
  206.         |  tm->tm_sec  ^ gtm->tm_sec)
  207.         return -1;
  208.  
  209.     tm->tm_wday = gtm->tm_wday;
  210.     return gt;
  211. }
  212.  
  213. /*
  214. * Check TM0 and convert it to time_t, assuming ZONE minutes west of gmtime.
  215. * Use localtime if ZONE is the special value LOCAL_TIME.
  216. * Ignore TM0->tm_yday; assume default values for any other negative inputs.
  217. * Yield -1 on failure.
  218. */
  219.     static time_t
  220. maketime(tm0, zone)
  221.     struct tm const *tm0;
  222.     int zone;
  223. {
  224.     int localzone, wday;
  225.     struct tm tm;
  226.     time_t r;
  227.  
  228.     localzone = zone==LOCAL_TIME;
  229.  
  230.     tm = *tm0;
  231.     if (tm.tm_year < 0) {
  232.         /* Set default year, month, day from current time.  */
  233.         register struct tm *d = time2tm(now(), localzone);
  234.         if (!localzone)
  235.         adjzone(d, -zone);
  236.         tm.tm_year = d->tm_year;
  237.         if (tm.tm_mon < 0) {
  238.         tm.tm_mon = d->tm_mon;
  239.         if (tm.tm_mday < 0)
  240.             tm.tm_mday = d->tm_mday;
  241.         }
  242.     }
  243.  
  244.     /* Set remaining default fields to be their minimum values.  */
  245.     if (tm.tm_mon < 0) tm.tm_mon = 0;
  246.     if (tm.tm_mday < 0) tm.tm_mday = 1;
  247.     if (tm.tm_hour < 0) tm.tm_hour = 0;
  248.     if (tm.tm_min < 0) tm.tm_min = 0;
  249.     if (tm.tm_sec < 0) tm.tm_sec = 0;
  250.  
  251.     if (!localzone)
  252.         adjzone(&tm, zone);
  253.     wday = tm.tm_wday;
  254.  
  255.     /* Convert and fill in the rest of the tm.  */
  256.     r = tm2time(&tm, localzone);
  257.  
  258.     /* Check weekday.  */
  259.     if (r != -1  &&  0 <= wday  &&  wday != tm.tm_wday)
  260.         return -1;
  261.  
  262.     return r;
  263. }
  264.  
  265. /*
  266. * Convert Unix time to RCS format.
  267. * For compatibility with older versions of RCS,
  268. * dates before AD 2000 are stored without the leading "19".
  269. */
  270.     void
  271. time2date(unixtime,date)
  272.     time_t unixtime;
  273.     char date[datesize];
  274. {
  275.     register struct tm const *tm = time2tm(unixtime, RCSversion<VERSION(5));
  276.     VOID sprintf(date, DATEFORM,
  277.         tm->tm_year  +  (tm->tm_year<100 ? 0 : TM_YEAR_ORIGIN),
  278.         tm->tm_mon+1, tm->tm_mday,
  279.         tm->tm_hour, tm->tm_min, tm->tm_sec
  280.     );
  281. }
  282.  
  283. /* Parse a free-format date in SOURCE, yielding a Unix format time.  */
  284.     static time_t
  285. str2time(source)
  286.     char const *source;
  287. {
  288.     int zone;
  289.     time_t unixtime;
  290.     struct tm parseddate;
  291.  
  292.     if (!partime(source, &parseddate, &zone))
  293.         faterror("can't parse date/time: %s", source);
  294.     if (LOCAL_TIME < zone)
  295.         /* No zone was specified.  */
  296.         zone = RCSversion<VERSION(5) ? LOCAL_TIME : 0;
  297.     if ((unixtime = maketime(&parseddate, zone))  ==  -1)
  298.         faterror("bad date/time: %s", source);
  299.     return unixtime;
  300. }
  301.  
  302. /*
  303. * Parse a free-format date in SOURCE, convert it
  304. * into RCS internal format, and store the result into TARGET.
  305. */
  306.     void
  307. str2date(source, target)
  308.     char const *source;
  309.     char target[datesize];
  310. {
  311.     time2date(str2time(source), target);
  312. }
  313.  
  314. /* Convert an RCS internal format date to time_t.  */
  315.     time_t
  316. date2time(source)
  317.     char const source[datesize];
  318. {
  319.     char s[datesize];
  320.     return str2time(date2str(source, s));
  321. }
  322.